//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
//------------------------------------------------------------------------------
//compiler stuff
//------------------------------------------------------------------------------
#include <stdarg.h>
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//ulinux stuff
//------------------------------------------------------------------------------
#include <ulinux/compiler_types.h>
#include <ulinux/types.h>
#include <ulinux/sysc.h>
#include <ulinux/error.h>

#include <ulinux/socket/socket.h>
#include <ulinux/socket/un.h>
#include <ulinux/socket/msg.h>

#include <ulinux/utils/ascii/ascii.h>
#include <ulinux/utils/ascii/string/string.h>
#include <ulinux/utils/ascii/string/vsprintf.h>
#include <ulinux/utils/mem.h>
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//protocol
//------------------------------------------------------------------------------
#include <wayland/display.h>
#include <wayland/callback.h>
#include <wayland/compositor.h>
#include <wayland/registry.h>
#include <wayland/shm.h>
#include <wayland/shm_pool.h>
#include <wayland/buffer.h>
#include <wayland/seat.h>
#include <wayland/output.h>
#include <wayland/keyboard.h>
#include <wayland/pointer.h>
#include <wayland/wayland.h>
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//our private stuff
//------------------------------------------------------------------------------
#include "globals.h"
#include "srv/registry.h"
#include "wire/wl_registry.h"
//------------------------------------------------------------------------------

#define REQ_BIND_DWS_MAX (1+1+1+(1+WL_ITFS_STRS_DWS_MAX)+1+1)
void wl_registry_req_bind(k_u32 registry_id,struct gbl *g,k_u32 id)
{
  k_u32 req[REQ_BIND_DWS_MAX];
  req[0]=registry_id;
  k_u itf_cstr_dws=wl_itfs_strs_dws[g->itf];
  k_u itf_cstr_sz=wl_itfs_strs_sz[g->itf];
  k_u16 ev_sz=(1+1+1+(1+itf_cstr_dws)+1+1)*sizeof(k_u32);
  req[1]=ev_sz<<16|WL_REGISTRY_BIND;
  req[2]=g->name;
  req[3]=itf_cstr_sz;
  u_a_strncpy((k_u8*)&req[4],wl_itfs_strs[g->itf],itf_cstr_dws*sizeof(k_u32));
  u_memset((k_u8*)&req[4]+itf_cstr_sz,0,itf_cstr_dws*sizeof(k_u32)-itf_cstr_sz);
  req[4+itf_cstr_dws]=1;//interface version
  req[4+itf_cstr_dws+1]=id;

  struct k_io_vec iov;
  iov.base=&req;
  iov.len=ev_sz;

  struct k_msg_hdr msg;
  u_memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  k_l r;
  do r=sysc(sendmsg,3,srv_so,&msg,0);while(r==-K_EINTR);

  if(K_ISERR(r)){
    PERR("FATAL(%d):unable to send the shm binding request\n",r);
    sysc(exit_group,1,-1);
  }
  if(r!=ev_sz){
    PERR("FATAL:registry::bind request only %ld/%ld was sent\n",r,ev_sz);
    sysc(exit_group,1,-1);
  }
  if(!r) sysc(exit_group,1,0);//server is gone
}

static void ev_global(k_u32 *ev,
                           void (*listener_global)(k_u32 name,enum wl_itfs itf))
{
  k_u16 dws=(ev[1]>>16)>>2;

  if(dws<5){
    PERRC("FATAL:registry::global event is incomplete\n");
    sysc(exit_group,1,-1);
  }

  //----------------------------------------------------------------------------
  //parse the interface name string
  k_u8 *end=(k_u8*)&ev[4];
  while(end<(k_u8*)(ev+dws-1)){
    if(*end==0) break;
    ++end;
  }

  if(end==(k_u8*)&ev[4]){
    PERRC("FATAL:registry::global event, interface name empty\n");
    sysc(exit_group,1,-1);
  }

  if(end==(k_u8*)(ev+dws-1)){
    PERR("FATAL:registry::global event, interface name misses terminator\n");
    sysc(exit_group,1,-1);
  }

  enum wl_itfs itf=wl_strn2itf((k_u8*)&ev[4],end-(k_u8*)&ev[4]+1);
  //----------------------------------------------------------------------------

  k_u32 name=ev[2];//get the server object name

  //XXX:ignore version for the moment

  listener_global(name,itf);
}

static void ev_global_remove(k_u32 *ev,
                                     void (*listener_global_remove)(k_u32 name))
{
  k_u16 dws=(ev[1]>>16)>>2;

  if(dws<3){
    PERRC("FATAL:registry::global_remove event, missing server object name\n");
    sysc(exit_group,1,-1);
  }

  listener_global_remove(ev[2]);
}

void wl_registry_ev(k_u32 *ev,void *listener)
{
  if(!listener) return;

  k_u16 op=ev[1]&0xffff;

  struct wl_registry_listener *registry_listener=listener;
  switch(op){
  case WL_REGISTRY_GLOBAL:
    ev_global(ev,registry_listener->global);
    break;
  case WL_REGISTRY_GLOBAL_REMOVE:
    ev_global_remove(ev,registry_listener->global_remove);
    break;
  }
}
